package find.prj.lists;

import java.util.Collection;
import java.util.List;
import java.util.ListIterator;

public class TwoWayList<T> extends AbstractListClass<T> {

	private TurboNode<T> FirstNode;
	private TurboNode<T> LastNode;
	
	public TwoWayList() {
		this.FirstNode = null;
		this.LastNode = null;
		this.Size = 0;
	}

	public TwoWayList(T value) {
		this();
		this.add(value);		
	}
	
	public TwoWayList(T[] valueArray) {
		this();
		ListIterator<T> iter = this.listIterator();
		for (T value : valueArray)
			iter.add(value);
	}

	public TwoWayList(Collection<T> list) {
		this();
		this.addAll(list);
	}
	
	@Override
	public void clear() {
		FirstNode = null;
		LastNode = null;
		Size = 0;
	}

	@SuppressWarnings("unchecked")
	private void setFirstNode(TurboNode<? extends Object> node) {
		FirstNode = (TurboNode<T>) node;
	}

	@SuppressWarnings("unchecked")
	private void setLastNode(TurboNode<? extends Object> node) {
		LastNode = (TurboNode<T>) node;
	}

	@Override
	public ListIterator<T> listIterator(int index) {
		return new TwoWayListIterator<T>(index);
	}

	@Override
	public List<T> subList(int fromIndex, int toIndex) {
		TwoWayList<T> newList = new TwoWayList<T>();
		ListIterator<T> iter = this.listIterator(fromIndex);
		while (iter.nextIndex() != toIndex)
			newList.add(iter.next());
		return newList;
	}
	
	// ----------------------------------- TwoWayListIterator ------------------------------------
	@SuppressWarnings("hiding")
	private class TwoWayListIterator<T> implements ListIterator<T> {

		private TurboNode<T> Cursor;
		private TurboNode<T> PreviousCursor;
		private int Iteration;
		
		@SuppressWarnings("unchecked")
		public TwoWayListIterator(int index) {
			if (index < Size/2) {
				Cursor = (TurboNode<T>) FirstNode;
				PreviousCursor = null;
				Iteration = 0;
				while (Iteration != index)
					next();
			}
			else {
				Cursor = null;
				PreviousCursor = (TurboNode<T>) LastNode;
				Iteration = Size;
				while (Iteration != index)
					previous();
			}
		}

		@SuppressWarnings("unchecked")
		@Override
		public void add(T e) {
			TurboNode<T> newNode = new TurboNode<T>(e);
			if (Iteration > 0)
				PreviousCursor.NextNode = newNode;
			newNode.PreviousNode = PreviousCursor;
			newNode.NextNode = Cursor;
			if(Iteration < Size)
				Cursor.PreviousNode = newNode;
			PreviousCursor = newNode;
			if(Iteration == 0)
				setFirstNode((TurboNode<Object>) newNode);
			if(Iteration == Size)
				setLastNode((TurboNode<Object>) newNode);
			Size++;
			Iteration++;
		}

		@Override
		public boolean hasNext() {
			if (Iteration < Size)
				return true;
			return false;
		}

		@Override
		public boolean hasPrevious() {
			if (Iteration > 0)
				return true;
			return false;
		}

		@Override
		public T next() {
			if ((Iteration >= Size) || (Iteration < 0))
					throw new IndexOutOfBoundsException();
			T value = Cursor.getValue();
			PreviousCursor = Cursor;
			Cursor = (TurboNode<T>) Cursor.NextNode;
			Iteration++;
			return value;
		}

		@Override
		public int nextIndex() {
			return Iteration;
		}

		@Override
		public T previous() {
			if ((Iteration > Size) || (Iteration <= 0))
					throw new IndexOutOfBoundsException();
			T value = PreviousCursor.getValue();
			Cursor = PreviousCursor;
			PreviousCursor = PreviousCursor.PreviousNode;
			Iteration--;
			return value;
		}

		@Override
		public int previousIndex() {
			return Iteration-1 ;
		}

		@Override
		public void remove() {
			if (Iteration > 0)
				PreviousCursor.NextNode = Cursor.NextNode;
			Cursor = (TurboNode<T>) Cursor.NextNode;
			if (Iteration < Size-1)
				Cursor.PreviousNode = PreviousCursor;
			if (Iteration == 0)
				setFirstNode(Cursor);
			if (Iteration == Size-1)
				setLastNode(PreviousCursor);
			Size--;
		}

		@Override
		public void set(T e) {
			Cursor.setValue(e);
		}
		
	}
	

}
